package com.jinke.api.common.util;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSONObject;
import com.github.rholder.retry.RetryException;
import com.github.rholder.retry.Retryer;
import com.google.common.collect.Maps;
import com.jinke.api.common.constant.RetryTypeConstants;
import com.jinke.api.common.exception.Asserts;
import com.jinke.api.common.retry.CommonRetryBuilder;
import com.jinke.api.common.retry.NeedRetryException;
import com.jinke.api.modules.third.huaLei.model.response.HuaLeiSelectAuthResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.stream.IntStream;

/**
 * @projectName:
 * @title: OKHttpRetryRequestUtil
 * @description: okhttp请求工具类
 * @see:
 * @author: hhp
 * @version: 1.0.0
 * @createTime: 2024/8/23 14:45
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class OKHttpRetryRequestUtil {
    private final CommonRetryBuilder retryBuilder;
    static final char[] CHARS = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
    /**
     * @Author: hhp
     * @Description: okhttp 发送 post 请求
     * @Data: 2024/8/23 10:46
     * @param java.lang.String classType 请求类类型 如果retry 需要按 某个类 的 某个 code 进行重试 传这个
     * @param java.lang.String mediaType eg: application/json
     * @param java.lang.String sendUrl 请求地址
     * @param java.lang.String param 请求参数
     * @param java.util.Map<java.lang.String, java.lang.String> addHeaders eg: key:"Content-Type", value:"application/json"
     * @param java.util.Map<java.lang.String, java.lang.String> headers eg: key:"Authorization", value:"bear tokenTest"
     * @return: java.lang.String 响应json串
     * @throws:
     **/
    /**
     * @Author: hhp
     * @Description: okhttp 发送 post 请求
     * @Data: 2024/8/30 13:15
     * @param java.lang.String classType 请求类类型 如果retry 需要按 某个类 的 某个 code 进行重试 传这个
     * @param java.lang.String mediaType eg: application/json
     * @param java.lang.String sendUrl 请求地址
     * @param java.lang.String param 请求参数
     * @param boolean needQueryParameter url是否需要加 queryParam
     * @param java.util.Map<java.lang.String, java.lang.String> queryParameterMap url queryParam eg: key:"param", value:"{name:'test', isTest:true}"
     * @param java.util.Map<java.lang.String, java.lang.String> addHeaders eg: key:"Content-Type", value:"application/json"
     * @param java.util.Map<java.lang.String, java.lang.String> headers eg: key:"Authorization", value:"bear tokenTest"
     * @return: java.lang.String
     * @throws:
     **/
    public String sendPostRequest(String classType, String mediaType, String sendUrl, String param, boolean needQueryParameter, Map<String, String> queryParameterMap, Map<String, String> addHeaders, Map<String, String> headers) {
        OkHttpClient client = new OkHttpClient().newBuilder()
                .build();
        RequestBody body = RequestBody.create(MediaType.parse(mediaType), param);

        Request.Builder builder = new Request.Builder();

        if (needQueryParameter) {
            HttpUrl.Builder urlBuilder = HttpUrl.parse(sendUrl).newBuilder();
            Optional.ofNullable(queryParameterMap).orElse(Maps.newHashMap()).forEach(urlBuilder::addQueryParameter);
            builder.url(urlBuilder.build().url());
        } else {
            builder.url(sendUrl);
        }

        Optional.ofNullable(headers).orElse(Maps.newHashMap()).forEach(builder::header);
        Optional.ofNullable(addHeaders).orElse(Maps.newHashMap()).forEach(builder::addHeader);

        Request request = builder.method("POST", body).build();
        return getResponse(classType, client, request);
    }

    /**
     * @Author: hhp
     * @Description: okhttp 发送 get 请求
     * @Data: 2024/8/23 10:58
     * @param java.lang.String classType 请求类类型 如果retry 需要按 某个类 的 某个 code 进行重试 传这个
     * @param java.lang.String sendUrl 请求地址
     * @param java.util.Map<java.lang.String, java.lang.String> headers eg: key:"Authorization", value:"bear tokenTest"
     * @return: java.lang.String 响应json串
     * @throws:
     **/
    public String sendGetRequest(String classType, String sendUrl, Map<String, String> headers) {
        OkHttpClient client = new OkHttpClient().newBuilder()
                .build();
        Request.Builder builder = new Request.Builder().url(sendUrl);
        Optional.ofNullable(headers).orElse(Maps.newHashMap()).forEach(builder::header);
        Request request = builder.build();
        return getResponse(classType, client, request);
    }

    /**
     * @Author: hhp
     * @Description: 拿到请求响应
     * @Data: 2024/8/23 11:01
     * @param java.lang.String classType 请求类类型 如果retry 需要按 某个类 的 某个 code 进行重试 传这个
     * @param okhttp3.OkHttpClient client OkHttpClient
     * @param okhttp3.Request request Request
     * @return: java.lang.String 响应json串
     * @throws:
     **/
    private String getResponse(String classType, OkHttpClient client, Request request) {
        // 只有主动抛出 needRetryException才会重试  其他任何异常都不会重试
        // 发送请求并获取响应
        Retryer build = retryBuilder.build();
        Callable<String> getTokeCall = ()->{
            Response response = client.newCall(request).execute();
            String responseBody = "";
            if (response.isSuccessful()) {
                // 处理返回的数据
                if (ObjectUtil.isNotNull(response.body())) {
                    // response.body().string取一次后 再次取会异常 这里不反悔response直接返回 string
                    responseBody = response.body().string();
                    if (classType.equals(RetryTypeConstants.HUA_LEI_SELECT_AUTH)) {
                        HuaLeiSelectAuthResponse huaLeiSelectAuthResponse = JSONObject.parseObject(Optional.of(responseBody).orElse("{}"), HuaLeiSelectAuthResponse.class);
                        if (Optional.ofNullable(huaLeiSelectAuthResponse.getAck()).orElse("false").equals("false")) {
                            // 华磊身份认证返回false 或 空 时 重试
                            throw new NeedRetryException("身份认证请求失败:" + responseBody);
                        }
                    }
                }
            } else {
                log.info("未请求成功 响应码:{}", response.code());
            }
            return responseBody;
        };
        String responseBody = null;
        try {
            responseBody = (String) build.call(getTokeCall);
        } catch (ExecutionException | RetryException e) {
            Asserts.fail("接口请求失败");
        }
        return responseBody;
    }

    public static String length20nonceStr() {
        Random random = new Random();
        char[] buffer = new char[20];
        IntStream.range(0, 20).forEach(i -> buffer[i] = CHARS[random.nextInt(CHARS.length)]);
        return new String(buffer);
    }
}
